package lxs.swift.verify.getters;

import lxs.swift.verify.Verifies;

import java.util.Collection;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.Predicate;

public interface Get<T, R> {
    R get(T v);

    static <T, R> Get<T, R> of(Get<T, R> getter) {
        return getter;
    }

    static <T> IntGet<T> ofInt(Get<T, Number> getter) {
        return (t) -> {
            Number num = getter.get(t);
            if (num == null) return null;
            return num.intValue();
        };
    }

    static <T> LongGet<T> ofLong(Get<T, Number> getter) {
        return (t) -> {
            Number num = getter.get(t);
            if (num == null) return null;
            return num.longValue();
        };
    }

    static <T> DoubleGet<T> ofDouble(Get<T, Number> getter) {
        return (t) -> {
            Number num = getter.get(t);
            if (num == null) return null;
            return num.doubleValue();
        };
    }

    static <T, V extends Comparable<V>> CompareGet<T, V> ofCompare(Get<T, V> getter) {
        return getter::get;
    }

    static <T> StrGet<T> ofStr(Get<T, String> getter) {
        return getter::get;
    }

    static <T, R> CollectGet<T, R> ofCollect(Get<T, Collection<R>> getter) {
        return getter::get;
    }

    default <RR> Get<T, RR> map(Function<R, RR> map) {
        Objects.requireNonNull(map);
        return (t) -> {
            R r = this.get(t);
            return map.apply(r);
        };
    }

    default IntGet<T> mapInt(Function<R, Integer> map) {
        Objects.requireNonNull(map);
        return (t) -> {
            R r = this.get(t);
            return map.apply(r);
        };
    }

    default LongGet<T> mapLong(Function<R, Long> map) {
        Objects.requireNonNull(map);
        return (t) -> {
            R r = this.get(t);
            return map.apply(r);
        };
    }

    default DoubleGet<T> mapDouble(Function<R, Double> map) {
        Objects.requireNonNull(map);
        return (t) -> {
            R r = this.get(t);
            return map.apply(r);
        };
    }

    default StrGet<T> mapStr(Function<R, String> map) {
        Objects.requireNonNull(map);
        return (t) -> {
            R r = this.get(t);
            return map.apply(r);
        };
    }

    default <V> CollectGet<T, V> mapCollect(Function<R, Collection<V>> map) {
        Objects.requireNonNull(map);
        return (t) -> {
            R r = this.get(t);
            return map.apply(r);
        };
    }

    default Predicate<T> eq(R r) {
        return (t) -> {
            R v = this.get(t);
            return Verifies.isObjectEq(v, r);
        };
    }

    default Predicate<T> neq(R r) {
        return this.eq(r).negate();
    }

    default Predicate<T> is(Predicate<R> predicate) {
        Objects.requireNonNull(predicate);
        return (t) -> {
            R r = this.get(t);
            return predicate.test(r);
        };
    }

    default Predicate<T> not(Predicate<R> predicate) {
        return this.is(predicate).negate();
    }

    default Predicate<T> isNull() {
        return (t) -> this.get(t) == null;
    }

    default Predicate<T> isDefault() {
        return (t) -> Verifies.isDefault(this.get(t));
    }

    default Predicate<T> isEmpty() {
        return (t) -> {
            R r = this.get(t);
            if (r == null) return true;
            return Verifies.isDefault(r);
        };
    }

    default Predicate<T> belongTo(Collection<R> values) {
        Objects.requireNonNull(values);
        return (t) -> {
            R r = this.get(t);
            return Verifies.include(r, values);
        };
    }

    default Predicate<T> belongTo(R... values) {
        Objects.requireNonNull(values);
        return (t) -> {
            R r = this.get(t);
            return Verifies.include(r, values);
        };
    }
}
